package SSF.OS.TCP;
import java.io.*;

/**
 * SendQueue contains all the unsent and sent-but-unacknowledged TCP data segments.
 * The data segments are wrapped in SendItems.
 * Additional data written to a tcp socket is segmented and added to the tail
 * of the SendQueue.
 * The acknowledged segment is removed from the head of the queue.
 * The segment to send next is indicated by an index named currentItem.
 */
class SendQueue {
  
  /**
   *  The head and the tail of the queue.
   */
  SendItem  dummyItem;

  /** The message that will be sent next */
  SendItem currentItem;

  SendItem maxSendItem;

  /** Thenumber of bytes currently in the send queue. */
  protected int len;

  /** The maximum segment size of one TCP packet. */
  int MSS = 1024;

  /** The maximum send queue size in bytes. */
  int TCP_BUFFER_SPACE = 16 * MSS;

  protected int segSize;

  /** Returns the number of bytes in the send queue. */
  int length(){return len;}

  /** Returns the amount of free space in the send queue. */
  int space(){return TCP_BUFFER_SPACE - len;}

  /* Constructor  */
  SendQueue(int MaxSS, int buffer){
    dummyItem = new SendItem();
    dummyItem.next = dummyItem.prev = dummyItem;
    MSS = MaxSS;
    TCP_BUFFER_SPACE = buffer * MSS;
    len = 0;
    currentItem = dummyItem;
    maxSendItem = dummyItem;
  } 

  public boolean hasMore(){return currentItem.next != dummyItem;}

  /** Return the next item to be sent from the send queue. */
  public SendItem nextTosend(){ 
    if(currentItem == maxSendItem){
      currentItem = currentItem.next;
      maxSendItem = currentItem;
    }else {
      currentItem = currentItem.next;
    }
    return currentItem;
  }

  public int nextMsgLen(){
    return currentItem.next.getMsgLen();
  }
  /** 
   * if the message can't be sent then move the index of current item back 
   * to the previous one
   */
  public void  MoveBack(SendItem item){ currentItem = item.prev;}

  /** print out all the message int the send queue*/
  public void printQ(){
    System.out.println( "Send data length: "+ len);
    int No = 1;
    SendItem s;
    for (s = dummyItem.next; s != dummyItem; s = s.next)
      System.out.println( No++ + ": " + s.seqno() + " length = "
                          + (s.msg).seglen );
  }

  // append() modified by ato 11/18/99
  // msgs appended by:
  // Rcv_WndManager.Rcv_WndInit appends SYNACK, size=0
  // tcpSession.ActiveOpen  appends SYN, size=0
  // tcpSession.sendReset appends RST, size=0  (not used)
  // tcpSession.close() appends FIN, size=0
  // tcpSession.dataFeed,  from socket write...

  /**
   * Appends virtual data to the send queue. The data is divided into
   * MSS-sized segments (with possible exception of the last segment) and wrapped
   * in SendItems.  Also used to send TCP SYN, FIN and RST messages.
   */ 
  public int append(ProtocolMessage msg, int size, TCP_Header tcp_h,
                    int SrcIPAddr, int DestIPAddr, boolean push) {
    int data_size = size;
    SendItem newitem;
    int oldlen = len;
    int freeSpace;

    if(msg instanceof TCP_Message){ // for SYN, FIN, RST messages only
      newitem = new SendItem();
      newitem.msg = (TCP_Message)msg;
      if (len==0) {
        dummyItem.next = newitem;
        newitem.prev = dummyItem;
        newitem.next = dummyItem;
        dummyItem.prev = newitem;
      }
      else {
        if (push){
          insert(newitem, currentItem);
          currentItem = newitem;
        }
        else {
          newitem.prev = dummyItem.prev;
          newitem.prev.next = newitem;
          newitem.next = dummyItem;
          dummyItem.prev = newitem;
        }
      }
    }

    // Append TCP data messages. If data_size > MSS, fragment the message
    // into as many TCP segments as free space allows.
    // If dataMessage carries a non-null object reference, put it as a payload
    // of the first segment only, and if data_size > MSS, pad the following
    // TCP messages with virtual data
    if(msg instanceof dataMessage){
      if(space() == 0 || data_size == 0)
        return 0;
      tcp_h.flags = 0;
      //1/25/02 TCP_Header tcp_header = new TCP_Header(tcp_h);
      newitem = new SendItem();
      segSize = Math.min(Math.min(MSS, data_size), space());
      //1/25/02 newitem.msg = new TCP_Message(tcp_header,SrcIPAddr,DestIPAddr,segSize);
      newitem.msg = new TCP_Message(tcp_h,SrcIPAddr,DestIPAddr,segSize);

      if(((dataMessage)msg).data != null)
        newitem.msg.carryPayload(msg);
      if (len==0) { // queue is empty
        dummyItem.next = newitem;
        newitem.prev = dummyItem;
      } else {
        newitem.prev = dummyItem.prev;
        newitem.prev.next = newitem;
      }
      newitem.next = dummyItem;
      dummyItem.prev = newitem;
      len += segSize;
      data_size -= segSize;

      // fill up any small leftover segment from previous append()
      if(data_size > 0) {
        if((currentItem.next != dummyItem) &&
	  (dummyItem.prev.msg.payload() == null)){
	  int n = MSS - (dummyItem.prev.msg).seglen;
	  if (n > 0) {
	    segSize = Math.min(n, data_size);
	    (dummyItem.prev.msg).seglen += segSize;
	    data_size -= segSize;
	    len += segSize;
	  }
        }
      }
      
      // create segments from the leftover data, if any 
      while(data_size > 0 && (len < TCP_BUFFER_SPACE)){
        newitem = new SendItem();
        segSize = Math.min(MSS, data_size);
        //1/25/02 newitem.msg = new TCP_Message(tcp_header,SrcIPAddr,DestIPAddr,segSize);
        newitem.msg = new TCP_Message(tcp_h,SrcIPAddr,DestIPAddr,segSize);
        newitem.prev = dummyItem.prev;
        newitem.prev.next = newitem;
        newitem.next = dummyItem;
        dummyItem.prev = newitem;
        data_size -= segSize;
        len += segSize;
      }
    }
    return(len - oldlen);
  }
  
  /** Insert item a before item b */
  public void insert(SendItem a, SendItem b){
    a.next = b;
    a.prev = b.prev;
    b.prev = a;
    a.prev.next = a;
  }

  /** Remove item a from the send queue when it was acked.
   *  Called by Send_WndManager.Rcv_Ack_Process()
   */
  public void remove(SendItem a){
    a.prev.next = a.next; 
    a.next.prev = a.prev;
    len -= (a.msg).seglen;
    if(a == currentItem)currentItem = a.prev;
  }

  /** Remove all SendItems from the send queue. Not used. */
  public void flush(){
    SendItem sd = dummyItem.next;
    SendItem next;

    while(sd != dummyItem){
      next = sd.next;
      (sd.msg).seglen = 0;
    }
    len = 0;
  }
}





